"""
API路由模块
提供RESTful API接口
"""
from flask import Blueprint, request, jsonify
from typing import Dict, Any, List
import os
from ..core.database import db
from ..processors.document_processor import DocumentProcessorFactory
from ..core.config import config
from ..core.vector_db import vector_db
from ..core.ai_service import ai_service

api = Blueprint('api', __name__)


@api.route('/health', methods=['GET'])
def health_check():
    """健康检查"""
    return jsonify({
        'status': 'ok',
        'message': 'Tiny QA is running'
    })


@api.route('/config', methods=['GET'])
def get_config():
    """获取配置"""
    # 检查是否在Docker环境中
    is_docker = os.path.exists('/.dockerenv')
    
    # 获取文档路径
    documents_path = config.get('documents.path')
    host_path = config.get('documents.host_path') if is_docker else None
    
    # 在Docker环境中，优先显示主机路径给用户
    display_path = host_path if is_docker and host_path else documents_path
    
    return jsonify({
        'documents_path': display_path,
        'container_path': documents_path if is_docker else None,
        'host_path': host_path if is_docker else None,
        'is_docker': is_docker,
        'supported_formats': config.get('documents.supported_formats'),
        'chunk_size': config.get('documents.chunk_size'),
        'chunk_overlap': config.get('documents.chunk_overlap'),
        'max_sources': config.get('qa.max_sources', 5),
        'source_content_length': config.get('qa.source_content_length', 200)
    })


@api.route('/chunking-config', methods=['GET'])
def get_chunking_config():
    """获取分块配置"""
    return jsonify({
        'strategy': config.get('documents.chunking.strategy', 'traditional'),
        'ai_model': config.get('documents.chunking.ai_model', 'N/A'),
        'fallback_to_traditional': config.get('documents.chunking.fallback_to_traditional', True)
    })


@api.route('/chunking-config', methods=['POST'])
def update_chunking_config():
    """更新分块配置"""
    data = request.get_json()
    
    if 'strategy' in data:
        config.set('documents.chunking.strategy', data['strategy'])
    
    if 'ai_model' in data:
        config.set('documents.chunking.ai_model', data['ai_model'])
    
    config.save()
    
    return jsonify({
        'message': '分块配置更新成功',
        'strategy': config.get('documents.chunking.strategy'),
        'ai_model': config.get('documents.chunking.ai_model')
    })


@api.route('/config', methods=['POST'])
def update_config():
    """更新配置"""
    data = request.get_json()
    
    if 'documents_path' in data:
        documents_path = data['documents_path'].strip()
        
        if not documents_path:
            return jsonify({'error': '文档目录路径不能为空'}), 400
        
        try:
            # 检查是否在Docker环境中
            is_docker = os.path.exists('/.dockerenv')
            
            if is_docker:
                # Docker环境：用户输入的是主机路径，需要映射到容器内部
                # 这里我们假设用户输入的路径会被挂载到 /app/data/documents
                container_path = '/app/data/documents'
                
                # 验证容器内部路径
                if not os.path.exists(container_path):
                    return jsonify({'error': '容器内文档目录不存在，请检查Docker卷挂载配置'}), 400
                
                if not os.path.isdir(container_path):
                    return jsonify({'error': '容器内路径不是目录'}), 400
                
                # 保存主机路径和容器路径
                config.set('documents.host_path', documents_path)  # 保存用户输入的主机路径
                config.set('documents.path', container_path)       # 保存容器内部路径
            else:
                # 非Docker环境：直接验证用户输入的路径
                if not os.path.exists(documents_path):
                    return jsonify({'error': '文档目录不存在'}), 400
                
                if not os.path.isdir(documents_path):
                    return jsonify({'error': '路径不是目录'}), 400
                
                # 保存路径配置
                config.set('documents.path', documents_path)
        
        except Exception as e:
            return jsonify({'error': f'路径验证失败: {str(e)}'}), 500
    
    if 'chunk_size' in data:
        config.set('documents.chunk_size', data['chunk_size'])
    
    if 'chunk_overlap' in data:
        config.set('documents.chunk_overlap', data['chunk_overlap'])
    
    if 'max_sources' in data:
        config.set('qa.max_sources', data['max_sources'])
    
    if 'source_content_length' in data:
        config.set('qa.source_content_length', data['source_content_length'])
    
    config.save()
    
    return jsonify({'message': '配置更新成功'})


@api.route('/documents', methods=['GET'])
def get_documents():
    """获取文档列表"""
    documents = db.get_all_documents()
    return jsonify({
        'documents': documents,
        'total': len(documents)
    })


@api.route('/documents/stats', methods=['GET'])
def get_documents_stats():
    """获取文档统计信息"""
    try:
        # 获取总文件数
        total_files = db.execute_query("SELECT COUNT(*) as count FROM documents")[0]['count']
        
        # 获取各类型文件数量
        pdf_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE file_type = '.pdf'")[0]['count']
        word_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE file_type = '.docx'")[0]['count']
        excel_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE file_type = '.xlsx'")[0]['count']
        txt_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE file_type = '.txt'")[0]['count']
        md_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE file_type = '.md'")[0]['count']
        
        # 获取处理状态统计
        processed_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE processed = TRUE")[0]['count']
        unprocessed_count = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE processed = FALSE")[0]['count']
        
        return jsonify({
            'total_files': total_files,
            'pdf_count': pdf_count,
            'word_count': word_count,
            'excel_count': excel_count,
            'txt_count': txt_count,
            'md_count': md_count,
            'processed_count': processed_count,
            'unprocessed_count': unprocessed_count
        })
        
    except Exception as e:
        return jsonify({'error': f'获取统计信息失败: {str(e)}'}), 500


@api.route('/documents/unprocessed', methods=['GET'])
def get_unprocessed_documents():
    """获取未处理的文档列表"""
    unprocessed_docs = db.execute_query(
        "SELECT * FROM documents WHERE processed = FALSE ORDER BY id"
    )
    return jsonify({
        'documents': unprocessed_docs,
        'total': len(unprocessed_docs)
    })


@api.route('/documents/scan', methods=['POST'])
def scan_documents():
    """扫描文档目录"""
    documents_path = config.get('documents.path', 'data/documents')
    
    if not os.path.exists(documents_path):
        return jsonify({'error': '文档目录不存在'}), 400
    
    scanned_files = []
    supported_formats = config.get('documents.supported_formats', [])
    
    for root, dirs, files in os.walk(documents_path):
        for file in files:
            file_path = os.path.join(root, file)
            _, ext = os.path.splitext(file.lower())
            
            if ext in supported_formats:
                file_size = os.path.getsize(file_path)
                relative_path = os.path.relpath(file_path, documents_path)
                
                # 检查是否已存在
                existing_doc = db.get_document(relative_path)
                if not existing_doc:
                    # 插入新文档
                    doc_id = db.insert_document(
                        file_path=relative_path,
                        file_name=file,
                        file_type=ext,
                        file_size=file_size
                    )
                    scanned_files.append({
                        'id': doc_id,
                        'file_path': relative_path,
                        'file_name': file,
                        'file_type': ext,
                        'file_size': file_size,
                        'processed': False
                    })
    
    return jsonify({
        'message': f'扫描完成，发现 {len(scanned_files)} 个新文档',
        'scanned_files': scanned_files
    })


@api.route('/documents/<int:doc_id>/process', methods=['POST'])
def process_document(doc_id: int):
    """处理单个文档"""
    # 获取文档信息
    result = db.execute_query("SELECT * FROM documents WHERE id = ?", (doc_id,))
    if not result:
        return jsonify({'error': '文档不存在'}), 404
    
    doc = result[0]
    documents_path = config.get('documents.path', 'data/documents')
    full_path = os.path.join(documents_path, doc['file_path'])
    
    if not os.path.exists(full_path):
        return jsonify({'error': '文件不存在'}), 404
    
    # 获取处理器
    processor = DocumentProcessorFactory.get_processor(full_path)
    if not processor:
        return jsonify({'error': '不支持的文件格式'}), 400
    
    # 处理文档
    result = processor.process_document(full_path)
    
    if result['success']:
        # 更新文档内容
        db.execute_update(
            "UPDATE documents SET content = ? WHERE id = ?",
            (result['text'], doc_id)
        )
        
        # 删除旧的块
        db.execute_update(
            "DELETE FROM document_chunks WHERE document_id = ?",
            (doc_id,)
        )
        
        # 插入新的块并生成向量
        for i, chunk in enumerate(result['chunks']):
            chunk_id = db.insert_chunk(doc_id, i, chunk)
            # 生成并存储向量
            vector_db.store_chunk_embeddings(chunk_id, chunk)
        
        # 更新处理状态
        db.update_document_processed(doc['file_path'], True, result['chunk_count'])
        
        return jsonify({
            'message': '文档处理成功',
            'chunk_count': result['chunk_count']
        })
    else:
        return jsonify({'error': f'文档处理失败: {result["error"]}'}), 500


@api.route('/documents/<int:doc_id>/reset', methods=['POST'])
def reset_document(doc_id: int):
    """重置单个文档（删除处理状态和块数据）"""
    try:
        # 检查文档是否存在
        result = db.execute_query("SELECT * FROM documents WHERE id = ?", (doc_id,))
        if not result:
            return jsonify({'error': '文档不存在'}), 404
        
        doc = result[0]
        
        # 删除文档块
        db.execute_update(
            "DELETE FROM document_chunks WHERE document_id = ?",
            (doc_id,)
        )
        
        # 重置文档状态
        db.execute_update(
            "UPDATE documents SET processed = FALSE, chunk_count = 0, content = NULL WHERE id = ?",
            (doc_id,)
        )
        
        return jsonify({
            'message': f'文档 {doc["file_name"]} 重置成功',
            'document_id': doc_id
        })
        
    except Exception as e:
        return jsonify({'error': f'重置文档失败: {str(e)}'}), 500


@api.route('/documents/process-all', methods=['POST'])
def process_all_documents():
    """处理所有未处理的文档"""
    unprocessed_docs = db.execute_query(
        "SELECT * FROM documents WHERE processed = FALSE"
    )
    
    results = []
    for doc in unprocessed_docs:
        try:
            # 调用处理单个文档的逻辑
            response = process_document(doc['id'])
            results.append({
                'doc_id': doc['id'],
                'file_name': doc['file_name'],
                'success': response.status_code == 200
            })
        except Exception as e:
            results.append({
                'doc_id': doc['id'],
                'file_name': doc['file_name'],
                'success': False,
                'error': str(e)
            })
    
    return jsonify({
        'message': f'批量处理完成，共处理 {len(unprocessed_docs)} 个文档',
        'results': results
    })


@api.route('/documents/reprocess-all', methods=['POST'])
def reprocess_all_documents():
    """重新处理所有文档（包括已处理的）"""
    all_docs = db.execute_query("SELECT * FROM documents")
    
    results = []
    for doc in all_docs:
        try:
            # 重置文档处理状态
            db.execute_update(
                "UPDATE documents SET processed = FALSE, chunk_count = 0 WHERE id = ?",
                (doc['id'],)
            )
            
            # 删除旧的块和向量
            db.execute_update(
                "DELETE FROM document_chunks WHERE document_id = ?",
                (doc['id'],)
            )
            
            # 调用处理单个文档的逻辑
            response = process_document(doc['id'])
            results.append({
                'doc_id': doc['id'],
                'file_name': doc['file_name'],
                'success': response.status_code == 200
            })
        except Exception as e:
            results.append({
                'doc_id': doc['id'],
                'file_name': doc['file_name'],
                'success': False,
                'error': str(e)
            })
    
    return jsonify({
        'message': f'重新处理完成，共处理 {len(all_docs)} 个文档',
        'results': results
    })


@api.route('/documents/reset', methods=['POST'])
def reset_documents():
    """重置文档数据库"""
    try:
        # 删除所有文档块
        db.execute_update("DELETE FROM document_chunks")
        
        # 删除所有文档记录
        db.execute_update("DELETE FROM documents")
        
        # 重置自增ID
        db.execute_update("DELETE FROM sqlite_sequence WHERE name IN ('documents', 'document_chunks')")
        
        # 清除向量文件
        vector_db_path = config.get('vector_db.path', 'data/vectors')
        if os.path.exists(vector_db_path):
            import glob
            vector_files = glob.glob(os.path.join(vector_db_path, '*.json'))
            for file in vector_files:
                try:
                    os.remove(file)
                except:
                    pass
        
        return jsonify({
            'message': '数据库重置成功',
            'deleted_chunks': 'all',
            'deleted_documents': 'all'
        })
        
    except Exception as e:
        return jsonify({'error': f'重置失败: {str(e)}'}), 500


@api.route('/qa', methods=['POST'])
def ask_question():
    """问答接口"""
    data = request.get_json()
    question = data.get('question', '').strip()
    
    if not question:
        return jsonify({'error': '问题不能为空'}), 400
    
    try:
        # 1. 向量搜索相关文档块
        max_sources = config.get('qa.max_sources', 5)
        relevant_chunks = vector_db.search_similar_chunks(question, top_k=max_sources)
        
        # 2. 生成AI回答
        if relevant_chunks:
            answer = ai_service.generate_qa_response(question, relevant_chunks)
        else:
            answer = "抱歉，我在文档中没有找到相关信息来回答您的问题。请尝试换个方式提问，或者先上传相关文档。"
        
        # 3. 准备来源信息
        source_content_length = config.get('qa.source_content_length', 200)
        sources = []
        for chunk in relevant_chunks:
            sources.append({
                'file_name': chunk['file_name'],
                'file_path': chunk['file_path'],
                'content': chunk['content'][:source_content_length] + '...' if len(chunk['content']) > source_content_length else chunk['content'],
                'similarity': round(chunk['similarity'], 3)
            })
        
        # 4. 保存问答历史
        db.insert_qa_history(question, answer)
        
        return jsonify({
            'question': question,
            'answer': answer,
            'sources': sources,
            'chunks_found': len(relevant_chunks)
        })
        
    except Exception as e:
        print(f"问答处理失败: {e}")
        return jsonify({
            'error': f'处理问题时发生错误: {str(e)}',
            'question': question,
            'answer': '抱歉，处理您的问题时遇到了技术问题，请稍后重试。',
            'sources': []
        }), 500


@api.route('/qa/history', methods=['GET'])
def get_qa_history():
    """获取问答历史"""
    limit = request.args.get('limit', 50, type=int)
    history = db.get_qa_history(limit)
    return jsonify({'history': history})


@api.route('/check-first-run', methods=['GET'])
def check_first_run():
    """检查是否需要首次运行配置"""
    documents_path = config.get('documents.path')
    first_run_required = not documents_path or documents_path == 'data/documents'
    
    return jsonify({
        'first_run_required': first_run_required,
        'has_config': bool(documents_path and documents_path != 'data/documents')
    })


@api.route('/validate-path', methods=['POST'])
def validate_path():
    """验证文档目录路径"""
    data = request.get_json()
    path = data.get('path', '').strip()
    
    if not path:
        return jsonify({'valid': False, 'error': '路径不能为空'}), 400
    
    try:
        # 检查是否在Docker环境中
        is_docker = os.path.exists('/.dockerenv')
        
        if is_docker:
            # Docker环境：用户输入的是主机路径，我们需要验证容器内的挂载点
            container_path = '/app/data/documents'
            
            if not os.path.exists(container_path):
                return jsonify({
                    'valid': False, 
                    'error': '容器内文档目录不存在，请检查Docker卷挂载配置',
                    'is_docker': True,
                    'suggestion': '请确保在docker-compose.yml中正确配置了卷挂载'
                }), 400
            
            if not os.path.isdir(container_path):
                return jsonify({
                    'valid': False, 
                    'error': '容器内路径不是目录',
                    'is_docker': True
                }), 400
            
            if not os.access(container_path, os.R_OK):
                return jsonify({
                    'valid': False, 
                    'error': '容器内目录无读取权限',
                    'is_docker': True
                }), 400
            
            # 使用容器内路径进行文件统计
            validation_path = container_path
        else:
            # 非Docker环境：直接验证用户输入的路径
            if not os.path.exists(path):
                return jsonify({'valid': False, 'error': '目录不存在'}), 400
            
            if not os.path.isdir(path):
                return jsonify({'valid': False, 'error': '路径不是目录'}), 400
            
            if not os.access(path, os.R_OK):
                return jsonify({'valid': False, 'error': '目录无读取权限'}), 400
            
            validation_path = path
        
        # 统计支持格式的文件数量和类型分布
        supported_formats = config.get('documents.supported_formats', ['.pdf', '.docx', '.xlsx', '.txt', '.md'])
        file_count = 0
        file_types = {}
        
        for root, dirs, files in os.walk(validation_path):
            for file in files:
                _, ext = os.path.splitext(file.lower())
                if ext in supported_formats:
                    file_count += 1
                    file_types[ext] = file_types.get(ext, 0) + 1
        
        # 转换为前端需要的格式
        file_types_list = [{'extension': ext, 'count': count} for ext, count in file_types.items()]
        
        return jsonify({
            'valid': True,
            'file_count': file_count,
            'file_types': file_types_list,
            'message': f'发现 {file_count} 个支持格式的文档文件',
            'is_docker': is_docker,
            'host_path': path if is_docker else None,
            'container_path': container_path if is_docker else None
        })
        
    except Exception as e:
        return jsonify({'valid': False, 'error': f'验证失败: {str(e)}'}), 500


@api.route('/first-run-config', methods=['POST'])
def first_run_config():
    """首次运行配置"""
    data = request.get_json()
    
    documents_path = data.get('documents_path', '').strip()
    chunk_size = data.get('chunk_size', 1000)
    chunk_overlap = data.get('chunk_overlap', 200)
    
    if not documents_path:
        return jsonify({'error': '文档目录路径不能为空'}), 400
    
    try:
        # 检查是否在Docker环境中
        is_docker = os.path.exists('/.dockerenv')
        
        if is_docker:
            # Docker环境：用户输入的是主机路径，需要映射到容器内部
            # 这里我们假设用户输入的路径会被挂载到 /app/data/documents
            # 实际路径验证将在容器内部的挂载点进行
            container_path = '/app/data/documents'
            
            # 验证容器内部路径
            if not os.path.exists(container_path):
                return jsonify({'error': '容器内文档目录不存在，请检查Docker卷挂载配置'}), 400
            
            if not os.path.isdir(container_path):
                return jsonify({'error': '容器内路径不是目录'}), 400
            
            # 保存主机路径和容器路径
            config.set('documents.host_path', documents_path)  # 保存用户输入的主机路径
            config.set('documents.path', container_path)       # 保存容器内部路径
        else:
            # 非Docker环境：直接验证用户输入的路径
            if not os.path.exists(documents_path):
                return jsonify({'error': '文档目录不存在'}), 400
            
            if not os.path.isdir(documents_path):
                return jsonify({'error': '路径不是目录'}), 400
            
            # 保存路径配置
            config.set('documents.path', documents_path)
        
        # 保存其他配置
        config.set('documents.chunk_size', chunk_size)
        config.set('documents.chunk_overlap', chunk_overlap)
        config.set('app.first_run_completed', True)
        config.save()
        
        return jsonify({
            'message': '首次配置完成',
            'documents_path': documents_path,
            'container_path': container_path if is_docker else documents_path,
            'chunk_size': chunk_size,
            'chunk_overlap': chunk_overlap,
            'is_docker': is_docker
        })
        
    except Exception as e:
        return jsonify({'error': f'配置失败: {str(e)}'}), 500


@api.route('/models', methods=['GET'])
def get_available_models():
    """获取可用的Ollama模型列表"""
    try:
        models = ai_service.get_available_models()
        return jsonify({
            'models': models,
            'current_model': ai_service.get_current_model()
        })
    except Exception as e:
        return jsonify({'error': f'获取模型列表失败: {str(e)}'}), 500


@api.route('/models', methods=['POST'])
def update_model():
    """更新当前使用的模型"""
    data = request.get_json()
    model_name = data.get('model_name', '').strip()
    
    if not model_name:
        return jsonify({'error': '模型名称不能为空'}), 400
    
    try:
        # 验证模型是否存在
        models = ai_service.get_available_models()
        model_names = [model.get('name', '') for model in models]
        
        if model_name not in model_names:
            return jsonify({'error': f'模型 {model_name} 不存在或未安装'}), 400
        
        # 更新配置
        for i, provider in enumerate(config.get('ai.providers', [])):
            if provider.get('name') == 'ollama':
                config.set(f'ai.providers.{i}.default_model', model_name)
                break
        config.save()
        
        # 更新AI服务的默认模型
        ai_service.update_default_model(model_name)
        
        return jsonify({
            'message': f'模型已更新为 {model_name}',
            'current_model': model_name
        })
        
    except Exception as e:
        return jsonify({'error': f'更新模型失败: {str(e)}'}), 500


@api.route('/status', methods=['GET'])
def get_status():
    """获取系统状态"""
    total_docs = db.execute_query("SELECT COUNT(*) as count FROM documents")[0]['count']
    processed_docs = db.execute_query("SELECT COUNT(*) as count FROM documents WHERE processed = TRUE")[0]['count']
    total_chunks = db.execute_query("SELECT COUNT(*) as count FROM document_chunks")[0]['count']
    
    # 检查AI服务状态
    ai_status = ai_service.test_connection()
    
    # 获取当前使用的模型
    current_model = ai_service.get_current_model()
    
    return jsonify({
        'total_documents': total_docs,
        'processed_documents': processed_docs,
        'total_chunks': total_chunks,
        'processing_rate': f"{processed_docs}/{total_docs}" if total_docs > 0 else "0/0",
        'ai_service_status': '正常' if ai_status else '异常',
        'current_model': current_model
    }) 